package com.tools.web.interceptor.quota;

import com.tools.common.exception.InvalidOperationException;
import com.tools.common.object.Note;
import com.tools.common.object.string.StrKit;
import com.tools.web.GlobalKit;
import com.tools.web.WebKit;
import com.tools.web.http.HttpDataFromEnum;
import com.tools.web.interceptor.auth.AuthUser;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

/**
 * 配额限制拦截器的部分抽象实现
 * */
@Note("配额限制拦截器的部分抽象实现")
public abstract class AbsQuotaManager implements QuotaManager {

    @Note("缓存 key 的前缀，后续拼接上 uniqueID 作为 key")
    private final String saveKeyPrefix;

    @Note("到达额度后响应给客户端的提示信息")
    private final String quotaErrorMessage;

    @Note("没有找到 UniqueID 参数所响应的异常信息")
    private final String notHaveUniqueIDErrorMessage;

    /* **************************************************************************************
     *
     *          构造器
     *
     * **************************************************************************************
     * */

    public AbsQuotaManager(QuotaSource source) {
        if(source == null) throw new NullPointerException("QuotaSource 不能为 null");
        this.saveKeyPrefix = source.getSaveKeyPrefix();
        this.quotaErrorMessage = source.getQuotaErrorMessage();
        this.notHaveUniqueIDErrorMessage = source.getNotHaveUniqueIDErrorMessage();
    }

    /* **************************************************************************************
     *
     *          核心逻辑
     *
     * **************************************************************************************
     * */

    @Override
    public final boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if(handler instanceof ResourceHttpRequestHandler) return true;
        HandlerMethod hm = (HandlerMethod) handler;
        Method method = hm.getMethod();
        QuotaLimit quotaLimit = method.getAnnotation(QuotaLimit.class);
        if(quotaLimit != null) {
            return this.protected_checkIsAllow(quotaLimit, request, response);
        }
        Class<?> typeClass = method.getDeclaringClass();
        QuotaLimit classQuotaLimit = typeClass.getAnnotation(QuotaLimit.class);
        return classQuotaLimit == null || this.protected_checkIsAllow(classQuotaLimit, request, response);
    }

    /* **************************************************************************************
     *
     *          提供给子类 / 需要子类实现的功能的方法
     *
     * **************************************************************************************
     * */

    @Note("子类需要自己实现的校验逻辑")
    protected abstract boolean protected_checkIsAllow(QuotaLimit quotaLimit, HttpServletRequest request, HttpServletResponse response) throws Exception;

    @Note("获取 key")
    protected final String protected_getKey(String action, String uniqueID) {
        return this.saveKeyPrefix + action + ':' + uniqueID;
    }

    public final String getSaveKeyPrefix() {
        return saveKeyPrefix;
    }

    public final String getQuotaErrorMessage() {
        return quotaErrorMessage;
    }

    public final String getNotHaveUniqueIDErrorMessage() {
        return notHaveUniqueIDErrorMessage;
    }

    @Note("获取限额后的错误信息。注解的优先级最高")
    protected final String protected_getQuotaErrorMessage(QuotaLimit quotaLimit, String uniqueID) {
        int code = quotaLimit.code();
        String msg = quotaLimit.msg();
        if(code <= 0 || msg.isEmpty()) {
            return this.quotaErrorMessage.replace("#{uniqueID}", uniqueID);
        }
        return "{\"code\":" + code + ", \"message\":\"" + msg.replace("#{uniqueID}", uniqueID) + "\"}";
    }

    @Note("获取没有得到唯一参数 uniqueID 后返回的错误信息。注解的优先级最高")
    protected final String protected_getNotHaveUniqueIDErrorMessage(QuotaLimit quotaLimit, String uniqueID) {
        int code = quotaLimit.code();
        String msg = quotaLimit.msg();
        if(code <= 0 || msg.isEmpty()) {
            return this.notHaveUniqueIDErrorMessage.replace("#{uniqueID}", uniqueID);
        }
        return "{\"code\":" + code + ", \"message\":\"" + msg.replace("#{uniqueID}", uniqueID) + "\"}";
    }

    @Note("从请求参数/IP/Request Attribute 里获取唯一标识作为 key 的后缀")
    protected final String protected_getUniqueID(QuotaLimit quotaLimit, HttpServletRequest request, HttpServletResponse response) throws Exception {
        String name = quotaLimit.name();
        String uniqueID;
        if("$IP".equals(name)) {
            uniqueID = (String) request.getAttribute(WebKit.CLIENT_IP_NAME);
        } else if("$ID".equals(name)) {
            AuthUser authUser = GlobalKit.getAuthUser(request);
            if(authUser == null) throw new InvalidOperationException("配额限制拦截器的执行顺序应该排在 AuthenticationManager 身份验证拦截器之后");
            uniqueID = authUser.getUserId().toString();
        } else {
            HttpDataFromEnum dataFrom = quotaLimit.from();
            uniqueID = dataFrom.getData(request, name);
        }
        if(StrKit.isBlank(uniqueID)) {
            response.getWriter().write(this.protected_getNotHaveUniqueIDErrorMessage(quotaLimit, uniqueID));
            return null;
        }
        return uniqueID;
    }



}
